<?php
/**
 * Svick a Fast Simple Smart PHP FrameWork
 * Author: Tommy 863758705@qq.com
 * Link: http://svick.tomener.com/
 * Since: 2022
 */

namespace Svick\Console;


use Svick\Core\App;
use Svick\Core\Engine;
use Svick\Exception\CoreException;
use Svick\Helper\Helper;
use Svick\OS\OS;

class Console
{
    protected $engine;

    protected $command = null;

    protected $controller = '';

    protected $action = '';

    protected $params = [];

    protected $map = [
        'serve' => [
            '-h' => 'host',
            '-p' => 'port',
            '-r' => 'root'
        ],
    ];

    private $commands = [
        '-v' => 'version',
        '-version' => 'version',
        '-h' => 'help',
    ];

    public function __construct()
    {
        $this->engine = new Engine();

        $this->parse();
    }

    public function start()
    {
        if ($this->command != 'init') {
            Engine::loadConfig();
        }

        if (method_exists($this, $this->command)) {
            call_user_func_array([$this, $this->command], []);
            return;
        }

        $seg = explode(':', $this->command);
        if (count($seg) != 2) {
            die("The command {$this->command} is not supported\n");
        }

        $this->parseRoute($seg);

        $ret = $this->runCommand();
        if ($ret) {
            return;
        }

        $this->runAction($this->controller, $this->action, array_values($this->params));
    }

    /**
     * 运行命令
     * 
     * @return bool
     */
    public function runCommand()
    {
        $handlerName = 'Svick\\Console\\' . $this->controller;
        $class_file = dirname(__FILE__) . DIRECTORY_SEPARATOR . $this->controller . '.php';
        if (!file_exists($class_file)) {
            return false;
        }

        $container = App::container();
        $handler = $container->get($handlerName);
        if (!method_exists($handler, $this->action)) {
            return false;
        }

        $data = call_user_func_array([$handler, $this->action], array_values($this->params));
        if ($data != null) {
            if (is_array($data)) {
                print_r($data);
            } elseif (is_object($data)) {
                var_dump($data);
            } else {
                echo $data;
            }
        }

        return true;
    }

    /**
     * 运行控制器方法
     *
     * @param $controller
     * @param $action
     * @param array $params
     * @throws CoreException
     */
    public function runAction($controller, $action, array $params = [])
    {
        App::iniSet($controller, $action, $params);

        $handlerName = 'cli\\' . $controller;
        $class_file = ROOT_PATH . str_replace('\\', DIRECTORY_SEPARATOR, $handlerName) . '.php';
        if (!file_exists($class_file)) {
            throw new CoreException('controller ' . $controller . ' not found.', 4004);
        }

        $container = App::container();
        $handler = $container->get($handlerName);
        if (!method_exists($handler, $action)) {
            throw new CoreException('Controller ' . $controller . ' has not method ' . $action, 40001);
        }

        $data = call_user_func_array([$handler, $action], $params);
        if ($data != null) {
            if (is_array($data)) {
                print_r($data);
            } elseif (is_object($data)) {
                var_dump($data);
            } else {
                echo $data;
            }
        }
    }

    /**
     * 框架版本号
     */
    public function version()
    {
        echo self::colorLightBlue('Svick PHP Framework v' . App::VERSION) . PHP_EOL;
    }

    /**
     * 帮助文档
     */
    public function help()
    {
        echo self::colorLightBlue('Usage') . ":\n\n"
            . " " . self::colorLightRed('-h help') . "     help\n"
            . " " . self::colorLightRed('-v version') . "  show framework version\n"
            . " " . self::colorLightRed('-init') . "       initialize project\n"
            . " " . self::colorLightRed('serve') . "       start development server\n"
            . " " . self::colorLightRed('route:cache') . " create route cache\n"
            . " " . self::colorLightRed('route:clear') . " clear route cache\n";
    }

    /**
     * 项目初始化
     */
    public function init()
    {
        $env_file = ROOT_PATH . 'config' . DS . 'env.config.php';
        $example_file = ROOT_PATH . 'config' . DS . 'env.example.config.php';

        if (file_exists($env_file)) {
            echo 'The project has been initialized' . PHP_EOL;
            return;
        }

        $content = file_get_contents($example_file);
        $content = str_replace(['SVICK_SALT', 'SVICK_AES_KEY'], [Helper::random(6), Helper::random(16)], $content);
        file_put_contents($env_file, $content);
        file_put_contents($example_file, $content);

        echo self::colorLightBlue('project init completed') . PHP_EOL;
    }

    /**
     * php svick serve -h 127.0.0.1 -p 8080 -r
     */
    public function serve()
    {
        $host = $this->params['host'] ?? '0.0.0.0';
        $port = $this->params['port'] ?? '8090';
        $root = $this->params['root'] ?? '';
        if (empty($root)) {
            $root = ROOT_PATH . 'public';
        }

        $command = sprintf(
            'php -S %s:%d -t %s %s',
            $host,
            $port,
            escapeshellarg($root),
            escapeshellarg($root . DIRECTORY_SEPARATOR . 'router.php')
        );

        if ($host == '0.0.0.0') {
            printf(self::colorGreen('Svick Development server is started On') . " http://%s:%s/\n", OS::ip(), $port);
            printf("                                     http://%s:%s/\n", '127.0.0.1', $port);
        } else {
            printf(self::colorGreen('Svick Development server is started On') . " http://%s:%s/\n", $host, $port);
        }
        printf("You can exit with `".self::colorLightRed("CTRL-C")."`\n");
        printf("Document root is: %s\n\n", $root);
        passthru($command);
    }

    /**
     * CLI模式解析路由
     *
     * @throws CoreException
     */
    private function parseRoute($router)
    {
        $this->controller = ucfirst(str_replace('/', '\\', $router[0]));
        $this->action = $router[1];
    }

    /**
     * 命令行解析
     */
    private function parse()
    {
        global $argv;
        global $argc;
        if ($argc < 2) {
            die("Lack of command parameters\n create project_name\n");
        }

        $this->command = $argv[1];
        if (isset($this->commands[$argv[1]])) {
            $this->command = $this->commands[$argv[1]];
        }

        $inputs = array_slice($argv, 2);
        $this->parseParams($inputs);
    }

    /**
     * 解析参数
     *
     * @param $inputs
     */
    private function parseParams($inputs)
    {
        $map = isset($this->map[$this->command]) ? $this->map[$this->command] : [];

        $key = '';
        foreach ($inputs as $input) {
            if (isset($map[$input])) {
                $key = $map[$input];
            } elseif (substr($input, 0, 1) == '-') {
                $key = substr($input, 1);
            } elseif (!empty($key)) {
                $this->params[$key] = $input;
                $key = '';
            } elseif (strpos($input, '=') !== false) {
                $seg = explode('=', $input);
                $this->params[$seg[0]] = $seg[1];
            }
        }
    }

    public static function colorLightRed($str)
    {
        return "\033[1;31m" . $str . "\033[0m";
    }

    public static function colorLightBlue($str)
    {
        return "\033[1;34m" . $str . "\033[0m";
    }

    public static function colorGreen($str)
    {
        return "\033[0;32m" . $str . "\033[0m";
    }
}
